﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using DL.PhotoCollage.Core;

namespace DL.PhotoCollage.Presentation.UserControls
{
    public partial class ImageDisplayUserControl : UserControl
    {
        private const int maximumAngle = 15;

        private readonly string filePath;

        private readonly CollagePresenter presenter;

        private double maximumSizeDiu;

        private double dpiScale;

        public ImageDisplayUserControl(string path, CollagePresenter presenterToUse)
        {
            this.filePath = path;
            this.presenter = presenterToUse;
            this.InitializeComponent();
        }

        public void FadeOutImage(Action<ImageDisplayUserControl, ICollageView> onCompletedAction, ICollageView view)
        {
            try
            {
                var storyboard = new Storyboard();
                var duration = new TimeSpan(0, 0, 1);

                var animation
                    = new DoubleAnimation
                    {
                        From = 1.0,
                        To = 0.0,
                        Duration = new Duration(duration)
                    };

                storyboard.Completed
                    += delegate
                    {
                        onCompletedAction(this, view);
                    };

                Storyboard.SetTargetName(animation, this.MainBorder.Name);
                Storyboard.SetTargetProperty(animation, new PropertyPath(OpacityProperty));
                storyboard.Children.Add(animation);
                storyboard.Begin(this);
            }
            catch (Exception ex)
            {
                this.presenter.HandleError(ex);
            }
        }

        private void ImageDisplayUserControl_OnInitialized(object sender, EventArgs e)
        {
            try
            {
                if (!this.presenter.Configuration.ShowPhotoBorder)
                {
                    this.MainBorder.BorderThickness = new Thickness(0);
                    this.InnerBorder.BorderThickness = new Thickness(0);
                }

                this.MainImage.MaxHeight = this.presenter.Configuration.MaximumSize;
                this.MainImage.MaxWidth = this.presenter.Configuration.MaximumSize;

                this.RotateImage();
                this.LoadImage();
            }
            catch (Exception ex)
            {
                this.presenter.HandleError(ex);
            }
        }

        private void RotateImage()
        {
            int angle = this.GetRandomAngle();
            var transform = new RotateTransform(angle);
            this.MainBorder.RenderTransform = transform;
        }

        private void LoadImage()
        {
            BitmapSource rawImage = this.GetRawImage();

            if (this.presenter.Configuration.IsGrayscale)
            {
                FormatConvertedBitmap grayImage = this.GetGrayscaleImage(rawImage);
                this.CalculateDpiScale(grayImage.DpiX);
                this.MainImage.Source = this.GetDisplayImage(grayImage);
            }
            else
            {
                this.CalculateDpiScale(rawImage.DpiX);
                this.MainImage.Source = this.GetDisplayImage(rawImage);
            }
        }

        private BitmapImage GetRawImage()
        {
            var image = new BitmapImage();
            image.BeginInit();
            image.CacheOption = BitmapCacheOption.None;
            image.CreateOptions = BitmapCreateOptions.IgnoreColorProfile | BitmapCreateOptions.IgnoreImageCache;
            image.UriSource = new Uri(this.filePath, UriKind.Absolute);
            image.EndInit();
            return image;
        }

        private FormatConvertedBitmap GetGrayscaleImage(BitmapSource source)
        {
            var image = new FormatConvertedBitmap();
            image.BeginInit();
            image.Source = source;
            image.DestinationFormat = PixelFormats.Gray32Float;
            image.EndInit();
            return image;
        }

        private ImageSource GetDisplayImage(BitmapSource rawImage)
        {
            if (this.DoesImageNeedScaling(rawImage.Height, rawImage.Width))
            {
                double scale = this.GetScale(rawImage);
                return this.GetScaledImage(rawImage, scale);
            }

            return rawImage;
        }

        private TransformedBitmap GetScaledImage(BitmapSource original, double scale)
        {
            var transform = new ScaleTransform(scale, scale);
            return new TransformedBitmap(original, transform);
        }

        private double GetScale(ImageSource image)
        {
            ImageOrientation orientation = this.GetImageOrientation(image.Height, image.Width);
            return orientation == ImageOrientation.Portrait
                    ? this.GetPortraitScale(image.Height)
                    : this.GetLandscapeScale(image.Width);
        }

        private int GetRandomAngle()
        {
            return this.presenter.GetRandomNumber(-maximumAngle, maximumAngle);
        }

        private void CalculateDpiScale(double imageDpi)
        {
            this.dpiScale = imageDpi / 96;
            this.maximumSizeDiu = this.presenter.Configuration.MaximumSize / this.dpiScale;
        }

        private bool DoesImageNeedScaling(double height, double width)
        {
            return height > this.maximumSizeDiu
                || width > this.maximumSizeDiu
                || this.dpiScale > 1;
        }

        private ImageOrientation GetImageOrientation(double height, double width)
        {
            return height > width
                ? ImageOrientation.Portrait
                : ImageOrientation.Landscape;
        }

        private double GetLandscapeScale(double width)
        {
            return (this.maximumSizeDiu / width) * this.dpiScale;
        }

        private double GetPortraitScale(double height)
        {
            return (this.maximumSizeDiu / height) * this.dpiScale;
        }
    }
}
